/**
 * 
 */
package it.uniroma3.engine.dictionary;

import it.uniroma3.beans.ProteinName;
import it.uniroma3.engine.indexer.Indexer;
import it.uniroma3.engine.model.CandidateProteinName;
import it.uniroma3.engine.paramer.EngineParameter;
import it.uniroma3.engine.util.Utils;
import it.uniroma3.persistence.ConnectionManager;
import it.uniroma3.persistence.DAOProteinName;
import it.uniroma3.persistence.PersistenceException;
import it.uniroma3.proteinHandlers.ProteinNamesFinder;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.io.IOUtils;

/**
 * @author Accosta Augusto
 * @author Lopez Elisabetta
 *
 */
public class ProteinNameIdentifierFacade {

	private List<String> knownNamesList;
//	private List<ProteinName> proteinNameList;
//	private List<ShortName> shortNameList;
	private Indexer indexer;
	private ProteinNameIdentifierController pniController;
	
	/**
	 * Constructor
	 */
	public ProteinNameIdentifierFacade() {
		
		/* 1) Retrieve proteins names from database */
		this.knownNamesList = ProteinNamesFinder.getKnownNamesList();
		
//		long startLoadDB = System.currentTimeMillis();
//		this.proteinNameList = this.loadDictionary(); // TODO load ProteinName and ShortName from DB
//		long endLoadDB = System.currentTimeMillis();
//		System.out.println("Dictionary loaded in " + (endLoadDB-startLoadDB) + " ms");
//		System.out.println("Protein list size: " + this.proteinList.size());
		
		/* Loads maps for spelled-out */
		Utils.spelledOutMapsPopulatorLocal();

		/* 2) Build and populate the Lucene index */
		// TODO abilitare solo se si vuole creare un indice in RAM (se si desidera farlo decommentare tutte le relative parti nelle altre classi)
		// TODO si può anche caricare in RAM un indice già creato che sta sul file system
//		this.buildIndex();
		
		this.pniController = new ProteinNameIdentifierController();
	}

	/**
	 * This is the starting point for the PNI searches
	 */
	// TODO ora è la facade la responsabile delle scritture su file
	@SuppressWarnings("static-access")
	public void launch(String entryTest) {
		List<CandidateProteinName> candidatesList = new LinkedList<CandidateProteinName>();
		
		candidatesList = this.pniController.discoverProtein(entryTest);
		
		candidatesList = Utils.mergeDuplicatedCandidatesAndRetrieveTotalScore(candidatesList);
		
		Collections.sort(candidatesList);
		// TODO ora lo faccio qui il normaliza score non più dentro ciascuna search
		// TODO qui c'è un eventuale postproecssamento (fatto con le distanze)
		candidatesList = Utils.postProcessingWithDistances(entryTest, candidatesList);
		
		Collections.sort(candidatesList);
		
		/*	TODO idee:
		 *  1) se i risultati non sono quelli attesi, dalla jsp che li fornisce, si può prevedere una funzione che ne filtri 
		 *  alcuni o che li ricalcoli in altro modo per quel dato input
		 *  2) si può pensare un post processamento simile a quello che fa eli sui token all'inizio del processo
		 *  3) Si può migliorare quello sui numeri romani individuandoli anche all'interno dell'input e non solo alla fine
		 */
		
		try {
			File directory = EngineParameter.FILE_OUTPUT_DIRECTORY;
			if(!directory.exists()) {
				directory.mkdir();
			}
			if(directory.isDirectory()) {
				File file = new File(entryTest.replaceAll("/", " ") + " (1).txt");
				IOUtils.writeLines(candidatesList, null, new FileOutputStream(directory.getPath() + directory.separator + file.getName()), "UTF-8");
			}
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			System.err.println("Fail! - Bads writings. " + e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * This method build the index starting from the list of proteins names loaded from database  
	 */
	public void buildIndex() {
		this.indexer = new Indexer();
		long startIndexCreation = System.currentTimeMillis();
		
		for (String proteinName : this.knownNamesList) {
			this.indexer.indexProtein(proteinName);
		}
		
//		for (ProteinName proteinName : this.proteinNameList) {
//			this.indexer.indexProtein(proteinName);
//		}
		
//		for (ShortName shortName : this.shortNameList) {
//			this.indexer.indexProtein(shortName);
//		}
		
		long endIndexCreation = System.currentTimeMillis();
		System.out.println("Index created in " + (endIndexCreation-startIndexCreation) + " ms");
		
		this.indexer.close();
	}
	
	/**
	 * This method retrieve the protein name from the database
	 * 
	 * @return the list of {@code ProteinName}
	 */
	@SuppressWarnings("unused")
	private List<ProteinName> loadDictionary() {
		
		/* DB Parameters */
		String driver = "oracle.jdbc.driver.OracleDriver";
		String server = "jdbc:oracle:thin:@localhost:1521:orcl";
		String log = "DCB_connectionpool.log";
		String user = "Bioinformatics";
		String password = "bioinformatics";
		int minConns = 2;
		int maxConns = 100;
		double maxConnTime = 3;
		String logAppendString = "true";
		int maxCheckoutSeconds = 60;
		int debugLevel = 0;
		int maxConnUsedTimes = 50;
		
		ConnectionManager.setConnectionsTest(driver, server, log, user, password, minConns, maxConns, maxConnTime, logAppendString, maxCheckoutSeconds, debugLevel, maxConnUsedTimes);
		List<ProteinName> proteinList = null;
		try {
			proteinList = DAOProteinName.doRetrieveAll();
		} catch (PersistenceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return proteinList;
	}

}
